package com.imcoke.abchat.web.service;

import com.imcoke.abchat.common.AppProperties;
import com.imcoke.abchat.common.ResultEnum;
import com.imcoke.abchat.domain.*;
import com.imcoke.abchat.domain.json.*;
import com.imcoke.abchat.web.mapper.UserMapper;
import com.imcoke.abchat.common.utils.EmailUtil;
import com.imcoke.abchat.common.utils.FileUtil;
import com.imcoke.abchat.common.utils.JwtTokenUtil;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author ccoke
 * @Description
 * @Date: 20:57 2017-11-27
 */
@Service
public class UserServiceImpl implements IUserService {
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    private UserMapper mUserMapper;
    private JwtTokenUtil mJwtTokenUtil;
    private AppProperties mAppProperties;
    private RedisTemplate<String, Object> mRedisTemplate;
    private EmailUtil mEmailUtil;
    @Autowired
    public UserServiceImpl(UserMapper mUserMapper, JwtTokenUtil mJwtTokenUtil, AppProperties mAppProperties, RedisTemplate<String, Object> mRedisTemplate, EmailUtil mEmailUtil) {
        this.mUserMapper = mUserMapper;
        this.mJwtTokenUtil = mJwtTokenUtil;
        this.mAppProperties = mAppProperties;
        this.mRedisTemplate = mRedisTemplate;
        this.mEmailUtil = mEmailUtil;
    }

    @Override
    public ResultBody firstGetUser(JsonRegister jsonRegister) throws Exception {
        //是否已经被注册
        if (mUserMapper.selectUserByUserName(jsonRegister.getUsername()) != null){
            return new ResultBody(ResultEnum.HAVE_ALREADY,"用户名已被注册");
        }
        if (mUserMapper.selectUserByEmail(jsonRegister.getEmail()) != null){
            return new ResultBody(ResultEnum.HAVE_ALREADY,"邮箱已被注册");
        }
        //将对象存入Redis,并设置过期时间
        mRedisTemplate.opsForValue().set(jsonRegister.getEmail(), jsonRegister);
        mRedisTemplate.expire(jsonRegister.getEmail(),mAppProperties.getExpiration_mail(), TimeUnit.SECONDS);

        //邮箱地址封至token
        Map<String,Object> claims = new HashMap<>();
        claims.put(AppProperties.EMAIL,jsonRegister.getEmail());
        String token = mJwtTokenUtil.generateToken(claims, mAppProperties.getExpiration_mail());
        logger.info("token:" + token);
        String url = mAppProperties.getUrl() + ":" + mAppProperties.getPort() + "/auth/register?token=" + token;
        //发送邮件
        mEmailUtil.sendMail(jsonRegister.getEmail(),"验证您的ABChat账号邮箱",mEmailUtil.getRegisterTemplate(jsonRegister,url));
        return new ResultBody(ResultEnum.SUCCESS,"已发送验证至邮箱");
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class)
    public Boolean secondGetUser(String token, StringBuilder message) throws IOException {

        //判断token是否过期或有效
        String mes = "无效的token";
        if ("".equals(token.trim())||token == null){
            message.append(mes);
            return false;
        }
        String email = null;
        try {
            Claims claims = mJwtTokenUtil.getClaimsFromToken(token);
            // 从token中获取邮箱
            email  = (String)claims.get(AppProperties.EMAIL);
            logger.info(email);
        }catch (Exception e){
            message.append(mes);
            return false;
        }
        //判断该邮箱是否存在redis
        JsonRegister jsonRegister = (JsonRegister) mRedisTemplate.opsForValue().get(email);
        if (jsonRegister == null){
            message.append(mes);
            return false;
        }
        //判断用户名或是否被注册
        User isHave = mUserMapper.selectUserByUserName(jsonRegister.getUsername());
        if(isHave != null){
            message.append("用户名已被注册");
            return false;
        }
        isHave = null;
        //在redis中删除该key
        mRedisTemplate.delete(jsonRegister.getEmail());
        // 将用户写入数据库
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        User user = new User();
        user.setUsername(jsonRegister.getUsername());
        user.setPassword(encoder.encode(jsonRegister.getPassword()));
        user.setEmail(jsonRegister.getEmail());
        user.setRegtime(new Date());
        //将username首字母变为头像
        //获取图片保存真实路径
        String realPath = mAppProperties.getFile_path() + mAppProperties.getImage_path()+ FileUtil.SEPARATOR;
        String urlPath = FileUtil.SEPARATOR +  mAppProperties.getImage_path() + FileUtil.SEPARATOR;
        user.setHeadimage(FileUtil.imageSave(user.getUsername(),realPath,urlPath));
        List<String> roleList = new ArrayList<>();
        roleList.add("ROLE_USER");
        user.setRoles(roleList);
        try{
            mUserMapper.insertUser(user);
            mUserMapper.insertRoles4User(user.getRoles(), user.getId());
            return true;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    @Override
    public ResultBody login(JsonLogin jsonLogin, Model model) {
        User user = null;
        //判断是邮箱还是账号
        if (jsonLogin.getAccount().contains("@")){
            user = mUserMapper.selectUserByEmail(jsonLogin.getAccount());
        }else {
            user = mUserMapper.selectUserByUserName(jsonLogin.getAccount());
        }
        if (user == null){
            return new ResultBody(ResultEnum.NOT_FOUND, "该用户不存在");
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        if (!encoder.matches(jsonLogin.getPassword(),user.getPassword())){
            return new ResultBody(ResultEnum.DENY, "账号或密码错误");
        }
        User temp = new User();
        temp.setId(user.getId());
        temp.setLasttime(new Date());
        //修改最后用户登录时间
        mUserMapper.updateUser(temp);
        Map<String,Object> claims = new HashMap<>();
        claims.put(AppProperties.USERNAME,user.getUsername());
        claims.put(AppProperties.KEYTIME,new Date());
        String token = mJwtTokenUtil.generateToken(claims, mAppProperties.getExpiration_login());
        model.addAttribute(AppProperties.USERNAME,user.getUsername());
        return new ResultBody(ResultEnum.SUCCESS, new JsonToken(token));
    }

    @Override
    public ResultBody firstGetForget(JsonEmail jsonEmail) throws Exception {
        //判断该email是否存在
        User user = mUserMapper.selectUserByEmail(jsonEmail.getEmail());
        if (user == null){
            return new ResultBody(ResultEnum.PARAMETER_NO,"该邮箱不存在");
        }
        //将邮箱号存入redis并设置过期时间
        Date date = new Date();
        mRedisTemplate.opsForValue().set(jsonEmail.getEmail(), date);
        mRedisTemplate.expire(jsonEmail.getEmail(),mAppProperties.getExpiration_mail(), TimeUnit.SECONDS);

        //邮箱地址封至token
        Map<String,Object> claims = new HashMap<>();
        claims.put(AppProperties.EMAIL,jsonEmail.getEmail());
        claims.put(AppProperties.KEYTIME,date);
        String token = mJwtTokenUtil.generateToken(claims, mAppProperties.getExpiration_mail());
        logger.info("token:" + token);
        String url = mAppProperties.getUrl() + ":" + mAppProperties.getPort() + "/auth/forget?token=" + token;
        //发送邮件
        mEmailUtil.sendMail(jsonEmail.getEmail(),"重设您的ABChat账号密码",mEmailUtil.getForgetTemplate(user.getUsername(),url));
        return new ResultBody(ResultEnum.SUCCESS,"已发送验证至邮箱");
    }

    @Override
    public Boolean secondGetForget(String token) {
        //判断token是否过期或有效
        if ("".equals(token.trim())||token == null){
            return false;
        }
        String email = null;
        Date date = null;
        try {
            Claims claims = mJwtTokenUtil.getClaimsFromToken(token);
            // 从token中获取邮箱
            email  = (String)claims.get(AppProperties.EMAIL);
            date = mJwtTokenUtil.getCreatedDateFromToken(token);
            logger.info(email);
        }catch (Exception e){
            return false;
        }
        //判断该邮箱是否存在redis
        Date cDate =  (Date)mRedisTemplate.opsForValue().get(email);
        if (cDate == null || !date.equals(cDate)){
            return false;
        }
        return true;
    }

    @Override
    public ResultBody resetPassword(JsonReset jsonReset,Model model) {
        ResultBody resultBody = new ResultBody(ResultEnum.OPTION_ERROR,"token已经过期，请重新发送");
        //判断token是否过期或有效
        if ("".equals(jsonReset.getToken().trim())||jsonReset.getToken() == null){
            return resultBody;
        }
        String email = null;
        Date date = null;
        try {
            Claims claims = mJwtTokenUtil.getClaimsFromToken(jsonReset.getToken());
            // 从token中获取邮箱
            email  = (String)claims.get(AppProperties.EMAIL);
            date = mJwtTokenUtil.getCreatedDateFromToken(jsonReset.getToken());
            logger.info(email);
        }catch (Exception e){
            return resultBody;
        }
        //判断该邮箱是否存在redis
        Date cDate =  (Date)mRedisTemplate.opsForValue().get(email);
        if (cDate == null || !date.equals(cDate)){
            return resultBody;
        }

        if(!jsonReset.getRepassword().equals(jsonReset.getNewpassword())){
            return new ResultBody(ResultEnum.OPTION_ERROR,"两次密码不相同");
        }

        //从redis中删除该key
        mRedisTemplate.delete(email);
        User u = mUserMapper.selectUserByEmail(email);
        //设置密码
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        User user = new User();
        user.setId(u.getId());
        user.setPassword(encoder.encode(jsonReset.getNewpassword()));
        user.setModifytime(new Date());
        mUserMapper.updateUser(user);
        model.addAttribute(AppProperties.USERNAME,u.getUsername());
        return new ResultBody(ResultEnum.SUCCESS,"重设密码成功！");
    }

    @Override
    public ResultBody firstEmail(JsonEmail jsonEmail) throws Exception {
        //判断邮箱是否为当前邮箱
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = mUserMapper.selectUserByUserName(username);
        if (jsonEmail.getEmail().equals(user.getEmail())){
            return new ResultBody(ResultEnum.OPTION_ERROR,"必须填写一个新的邮箱地址！");
        }
        //判断邮箱是否已被注册
        if (mUserMapper.selectUserByEmail(jsonEmail.getEmail()) != null){
            return new ResultBody(ResultEnum.HAVE_ALREADY,"邮箱已被注册");
        }
        //将对象存入Redis,并设置过期时间

        RedisObject redisObject = new RedisObject(username,new Date());
        mRedisTemplate.opsForValue().set(jsonEmail.getEmail(),redisObject );
        mRedisTemplate.expire(jsonEmail.getEmail(),mAppProperties.getExpiration_mail(), TimeUnit.SECONDS);

        //邮箱地址封至token
        Map<String,Object> claims = new HashMap<>();
        claims.put(AppProperties.EMAIL,jsonEmail.getEmail());
        claims.put(AppProperties.KEYTIME,redisObject.getCreated());
        String token = mJwtTokenUtil.generateToken(claims, mAppProperties.getExpiration_mail());
        logger.info("token:" + token);
        String url = mAppProperties.getUrl() + ":" + mAppProperties.getPort() + "/auth/email?token=" + token;
        //发送邮件
        mEmailUtil.sendMail(jsonEmail.getEmail(),"重设您的ABChat邮箱地址",mEmailUtil.getResetTemplate(user.getUsername(),url));
        return new ResultBody(ResultEnum.SUCCESS,"已发送验证至邮箱");
    }

    @Override
    public Boolean secondEmail(String token, Model model) throws IOException {
        //判断token是否过期或有效
        String message = "无效的token";
        if ("".equals(token.trim())||token == null){
            model.addAttribute("message", message);
            return false;
        }
        String email = null;
        Date date = null;
        try {
            Claims claims = mJwtTokenUtil.getClaimsFromToken(token);
            // 从token中获取邮箱
            email  = (String)claims.get(AppProperties.EMAIL);
            date = mJwtTokenUtil.getCreatedDateFromToken(token);
            logger.info(email);
        }catch (Exception e){
            model.addAttribute("message", message);
            return false;
        }
        //判断该邮箱是否存在redis
        RedisObject redisObject = (RedisObject) mRedisTemplate.opsForValue().get(email);
        if (redisObject == null){
            model.addAttribute("message", message);
            return false;
        }
        //判断是否为最新token
        if (!date.equals(redisObject.getCreated())){
            model.addAttribute("message", message);
            return false;
        }
        //判断邮箱是否被注册
        User isHave = mUserMapper.selectUserByEmail(email);
        if(isHave != null){
            model.addAttribute("message","邮箱已被注册");
            return false;
        }
        isHave = null;

        //在redis中删除该key
        mRedisTemplate.delete(email);
        //修改密码
        String username = (String)redisObject.getContent();
        User user = mUserMapper.selectUserByUserName(username);
        User temp = new User();
        temp.setId(user.getId());
        temp.setEmail(email);
        temp.setModifytime(new Date());
        mUserMapper.updateUser(temp);
        model.addAttribute(AppProperties.USERNAME,user.getUsername());
        return true;
    }

    @Override
    public ResultBody modifyPassword(JsonPassword jsonPassword) {
        //验证密码是否正确
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = mUserMapper.selectUserByUserName(username);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if(!encoder.matches(jsonPassword.getOldpassword(),user.getPassword())){
            return new ResultBody(ResultEnum.DENY,"原密码错误");
        }
        if (!jsonPassword.getNewpassword().equals(jsonPassword.getOldpassword())){
            return new ResultBody(ResultEnum.OPTION_ERROR,"两次密码输入不一致");
        }
        User temp = new User();
        temp.setId(user.getId());
        temp.setPassword(encoder.encode(jsonPassword.getNewpassword()));
        temp.setModifytime(new Date());
        mUserMapper.updateUser(temp);

        return new ResultBody(ResultEnum.SUCCESS,"修改密码成功！");
    }

    @Override
    public ResultBody modifyBasic(JsonBasicUser jsonBasicUser) {
        Boolean flag = false;
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = mUserMapper.selectUserByUserName(username);
        User temp = new User();
        temp.setId(user.getId());
        //修改昵称
        if (jsonBasicUser.getNickname()!=null){
            temp.setNickname(jsonBasicUser.getNickname());
            flag = true;
        }
        //修改头像
        if (jsonBasicUser.getHeadimage()!=null && !jsonBasicUser.getHeadimage().equals(user.getHeadimage())){
            //将现有头像删除
            String realPath = mAppProperties.getFile_path() + user.getHeadimage().substring(1);
            FileUtil.imageDelete(realPath);
            //设置新头像
            temp.setHeadimage(jsonBasicUser.getHeadimage());
            flag = true;
        }

        if (flag){
            mUserMapper.updateUser(temp);
            return new ResultBody(ResultEnum.SUCCESS,"修改基本信息成功！");
        }else {
            return new ResultBody(ResultEnum.PARAMETER_NO,"未做出任何更改！");
        }


    }

    @Override
    public ResultBody checkUsername(String username) {
        User user = mUserMapper.selectUserByUserName(username);
        if (user != null){
            return new ResultBody(ResultEnum.HAVE_ALREADY,"用户名已存在");
        }
        return new ResultBody(ResultEnum.SUCCESS,"用户名可用");
    }

    @Override
    public ResultBody checkEmail(String email) {
        User user = mUserMapper.selectUserByEmail(email);
        if (user != null){
            return new ResultBody(ResultEnum.HAVE_ALREADY,"邮箱地址已存在");
        }
        return new ResultBody(ResultEnum.SUCCESS,"邮箱地址可用");
    }
}
